import pandas as pd
import numpy as np
from numpy.linalg import eigh
from datetime import datetime
import seaborn as sns
sns.set(color_codes=True)
import matplotlib.pyplot as plt
%matplotlib inline
!pip install category_encoders
import category_encoders as ce
from scipy.stats import chi2_contingency
import sklearn as sklearn
from sklearn import metrics
from sklearn.preprocessing import StandardScaler, OneHotEncoder, LabelEncoder, OrdinalEncoder
from sklearn.covariance import EllipticEnvelope
from sklearn.pipeline import Pipeline
from sklearn.decomposition import PCA, FactorAnalysis, KernelPCA, SparsePCA
from sklearn.manifold import TSNE, Isomap
from sklearn.feature_selection import SelectFromModel
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import GradientBoostingClassifier, IsolationForest
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.metrics import silhouette_score
!pip install umap-learn
from umap import UMAP
Requirement already satisfied: category_encoders in /usr/local/lib/python3.10/dist-packages (2.6.3) Requirement already satisfied: numpy>=1.14.0 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (1.23.5) Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (1.2.2) Requirement already satisfied: scipy>=1.0.0 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (1.11.4) Requirement already satisfied: statsmodels>=0.9.0 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (0.14.1) Requirement already satisfied: pandas>=1.0.5 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (1.5.3) Requirement already satisfied: patsy>=0.5.1 in /usr/local/lib/python3.10/dist-packages (from category_encoders) (0.5.6) Requirement already satisfied: python-dateutil>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.0.5->category_encoders) (2.8.2) Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas>=1.0.5->category_encoders) (2023.3.post1) Requirement already satisfied: six in /usr/local/lib/python3.10/dist-packages (from patsy>=0.5.1->category_encoders) (1.16.0) Requirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.20.0->category_encoders) (1.3.2) Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.20.0->category_encoders) (3.2.0) Requirement already satisfied: packaging>=21.3 in /usr/local/lib/python3.10/dist-packages (from statsmodels>=0.9.0->category_encoders) (23.2) Requirement already satisfied: umap-learn in /usr/local/lib/python3.10/dist-packages (0.5.5) Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (1.23.5) Requirement already satisfied: scipy>=1.3.1 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (1.11.4) Requirement already satisfied: scikit-learn>=0.22 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (1.2.2) Requirement already satisfied: numba>=0.51.2 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (0.58.1) Requirement already satisfied: pynndescent>=0.5 in /usr/local/lib/python3.10/dist-packages (from umap-learn) (0.5.11) Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from umap-learn) (4.66.1) Requirement already satisfied: llvmlite<0.42,>=0.41.0dev0 in /usr/local/lib/python3.10/dist-packages (from numba>=0.51.2->umap-learn) (0.41.1) Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.10/dist-packages (from pynndescent>=0.5->umap-learn) (1.3.2) Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn>=0.22->umap-learn) (3.2.0)
Una vez hemos llamado a las potenciales librerías que podemos utilizar, procederemos a importar el DS: "siniestros_tiny". Esta base de datos: "Transport Canada’s National Collision Database (NCDB)" contiene datos sobre todas las colisiones de vehículos de motor notificables en Canadá que las provincias y territorios proporcionan cada año.
x = pd.read_csv("/siniestros_tiny.csv")
x.head(5)
| C_YEAR | C_MNTH | C_WDAY | C_HOUR | C_SEV | C_VEHS | C_CONF | C_RCFG | C_WTHR | C_RSUR | ... | V_ID | V_TYPE | V_YEAR | P_ID | P_SEX | P_AGE | P_PSN | P_ISEV | P_SAFE | P_USER | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2001 | 5 | 5 | 16 | 2 | 02 | 33 | 02 | 1 | 1 | ... | 01 | 01 | 1986 | 02 | M | 15 | 13 | 1 | UU | 2 |
| 1 | 2008 | 10 | 3 | 13 | 2 | 02 | 21 | 02 | 1 | 1 | ... | 02 | 05 | 2002 | 02 | M | 04 | 23 | 1 | 02 | 2 |
| 2 | 2009 | 8 | 7 | 09 | 2 | 02 | 36 | 02 | 1 | 1 | ... | 2 | 01 | 2004 | 02 | F | 17 | 21 | 2 | 02 | 2 |
| 3 | 2003 | 4 | 5 | 18 | 2 | 2 | UU | UU | U | U | ... | 2 | 01 | 1996 | 03 | F | 02 | 21 | 2 | 02 | 2 |
| 4 | 2014 | 9 | 1 | 07 | 2 | 03 | 21 | 01 | 1 | 1 | ... | 2 | 01 | 2001 | 01 | F | 36 | 11 | 2 | 02 | 1 |
5 rows × 22 columns
Una vez importados los datos, limpiaremos los nombres de las columnas. Pondremos todas las letras de los nombres de las columnas en minúscula, utilizando la función map() de esta manera será más fácil operar con ellos.
x.columns = map(str.lower,x.columns)
x.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 20000 entries, 0 to 19999 Data columns (total 22 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 c_year 20000 non-null int64 1 c_mnth 20000 non-null object 2 c_wday 20000 non-null object 3 c_hour 20000 non-null object 4 c_sev 20000 non-null int64 5 c_vehs 20000 non-null object 6 c_conf 20000 non-null object 7 c_rcfg 20000 non-null object 8 c_wthr 20000 non-null object 9 c_rsur 20000 non-null object 10 c_raln 20000 non-null object 11 c_traf 20000 non-null object 12 v_id 20000 non-null object 13 v_type 20000 non-null object 14 v_year 20000 non-null object 15 p_id 20000 non-null object 16 p_sex 20000 non-null object 17 p_age 20000 non-null object 18 p_psn 20000 non-null object 19 p_isev 20000 non-null object 20 p_safe 20000 non-null object 21 p_user 20000 non-null object dtypes: int64(2), object(20) memory usage: 3.4+ MB
Para esta primera parte realizaremos un análisis descriptivo enfocado en la relación de las variables con la severidad de las colisiones, llevaremos esto a cabo mediante un correlograma.
mcorr = x.corr()
sns.set(style="white")
plt.figure(figsize=(4, 4))
sns.heatmap(mcorr, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.subplots_adjust(bottom=0.2)
plt.title('Matriz de Correlaciones')
plt.show()
<ipython-input-4-1f336dded474>:1: FutureWarning: The default value of numeric_only in DataFrame.corr is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning. mcorr = x.corr()
Como podemos observar, realizar la matriz de correlaciones sin un análisis exploratorio de los datos no tiene sentido. Sin embargo, para continuar con la práctica comprobaremos el tipo de datos de las diferentes columnas, debido a que el hecho de que solo aparezcan dos de las variables, estará relacionado con el tipo de datos de las columnas, para ello utilizaremos la función info().
x.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 20000 entries, 0 to 19999 Data columns (total 22 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 c_year 20000 non-null int64 1 c_mnth 20000 non-null object 2 c_wday 20000 non-null object 3 c_hour 20000 non-null object 4 c_sev 20000 non-null int64 5 c_vehs 20000 non-null object 6 c_conf 20000 non-null object 7 c_rcfg 20000 non-null object 8 c_wthr 20000 non-null object 9 c_rsur 20000 non-null object 10 c_raln 20000 non-null object 11 c_traf 20000 non-null object 12 v_id 20000 non-null object 13 v_type 20000 non-null object 14 v_year 20000 non-null object 15 p_id 20000 non-null object 16 p_sex 20000 non-null object 17 p_age 20000 non-null object 18 p_psn 20000 non-null object 19 p_isev 20000 non-null object 20 p_safe 20000 non-null object 21 p_user 20000 non-null object dtypes: int64(2), object(20) memory usage: 3.4+ MB
Como podemos ver, se cumple la hipótesis de que el principal problema del correlograma es el tipo de datos de las columnas. Las dos variables que aparecen en el correlograma superior son: "c_year" y "c_sev" que coinciden con las columnas que tienen datos del tipo int64. Es por esto por lo que tendremos que seleccionar todas aquellas columnas que sean del tipo object y recategorizarlas mediante el OneHotEncoder.
catcols = list(x.select_dtypes("object").columns)
list_other = list(set(x.columns)-set(catcols))
ohe = ce.OneHotEncoder(cols=catcols)
model = ohe.fit(x)
model
OneHotEncoder(cols=['c_mnth', 'c_wday', 'c_hour', 'c_vehs', 'c_conf', 'c_rcfg',
'c_wthr', 'c_rsur', 'c_raln', 'c_traf', 'v_id', 'v_type',
'v_year', 'p_id', 'p_sex', 'p_age', 'p_psn', 'p_isev',
'p_safe', 'p_user'])In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. OneHotEncoder(cols=['c_mnth', 'c_wday', 'c_hour', 'c_vehs', 'c_conf', 'c_rcfg',
'c_wthr', 'c_rsur', 'c_raln', 'c_traf', 'v_id', 'v_type',
'v_year', 'p_id', 'p_sex', 'p_age', 'p_psn', 'p_isev',
'p_safe', 'p_user'])xtrans = model.transform(x)
xtrans.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 20000 entries, 0 to 19999 Columns: 441 entries, c_year to p_user_6 dtypes: int64(441) memory usage: 67.3 MB
Como se puede observar mediante el uso de describe() el OneHotEncoder no nos sería útil en este caso, ya que mediante el uso de este método, al haber muchas diferentes categorías dentro de las variables, nos genera muchas columnas, en este caso 441, por lo que al realizar un correlograma aunque lo hagamos de unas dimensiones muy grandes, no se podrá apreciar el heatmap debido a que los números se superpodrán unos a otros y no se entenderá nada.
Es por ello por lo que he considerado que sería más interesante aplicar un OrdinalEncoder, que resulta útil cuando tenemos datos ordinales, es decir, aquellos en los que se puede establecer un orden entre sus categorías.
columnas_categoricas = ['c_mnth', 'c_wday', 'c_hour', 'c_vehs', 'c_conf', 'c_rcfg',
'c_wthr', 'c_rsur', 'c_raln', 'c_traf', 'v_id', 'v_type',
'v_year', 'p_id', 'p_sex', 'p_age', 'p_psn', 'p_isev',
'p_safe', 'p_user']
xcat = x[columnas_categoricas]
oe = OrdinalEncoder()
catencod = pd.DataFrame(oe.fit_transform(xcat), columns=columnas_categoricas)
x[columnas_categoricas] = catencod
x[columnas_categoricas] = x[columnas_categoricas].astype('int64')
x.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 20000 entries, 0 to 19999 Data columns (total 22 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 c_year 20000 non-null int64 1 c_mnth 20000 non-null int64 2 c_wday 20000 non-null int64 3 c_hour 20000 non-null int64 4 c_sev 20000 non-null int64 5 c_vehs 20000 non-null int64 6 c_conf 20000 non-null int64 7 c_rcfg 20000 non-null int64 8 c_wthr 20000 non-null int64 9 c_rsur 20000 non-null int64 10 c_raln 20000 non-null int64 11 c_traf 20000 non-null int64 12 v_id 20000 non-null int64 13 v_type 20000 non-null int64 14 v_year 20000 non-null int64 15 p_id 20000 non-null int64 16 p_sex 20000 non-null int64 17 p_age 20000 non-null int64 18 p_psn 20000 non-null int64 19 p_isev 20000 non-null int64 20 p_safe 20000 non-null int64 21 p_user 20000 non-null int64 dtypes: int64(22) memory usage: 3.4 MB
Una vez comprobado que tras la recategorización de las variables a int64 no hemos perdido ningún valor, vamos a estandarizar las variables del conjunto de datos mediante la función StandardScaler.
scaler = StandardScaler()
model_scaled = scaler.fit(x)
xscal = pd.DataFrame(scaler.transform(x), columns=x.columns, index=x.index)
xscal.describe()
| c_year | c_mnth | c_wday | c_hour | c_sev | c_vehs | c_conf | c_rcfg | c_wthr | c_rsur | ... | v_id | v_type | v_year | p_id | p_sex | p_age | p_psn | p_isev | p_safe | p_user | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | ... | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 | 2.000000e+04 |
| mean | -6.189182e-15 | -4.192202e-17 | 8.792966e-17 | -1.293188e-16 | -3.101519e-16 | 1.953993e-17 | 1.705303e-17 | 7.709389e-17 | 8.242296e-17 | 1.065814e-17 | ... | 1.669775e-17 | -6.394885e-18 | 3.861800e-16 | -4.263256e-18 | 6.075140e-17 | 1.209699e-16 | -1.421085e-18 | -2.842171e-17 | -3.055334e-17 | 5.186962e-17 |
| std | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | ... | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 | 1.000025e+00 |
| min | -1.523723e+00 | -2.018871e+00 | -1.560344e+00 | -2.621201e+00 | -7.720496e+00 | -1.037241e+00 | -1.779513e+00 | -5.861629e-01 | -5.028017e-01 | -4.590698e-01 | ... | -7.636587e-01 | -3.594213e-01 | -4.359065e+00 | -3.407777e-01 | -9.746319e-01 | -1.670050e+00 | -4.936135e-01 | -9.122278e-01 | -9.923017e-01 | -5.593176e-01 |
| 25% | -8.694182e-01 | -9.431724e-01 | -1.042705e+00 | -7.196856e-01 | 1.295254e-01 | -9.306248e-01 | -6.552493e-01 | -5.861629e-01 | -5.028017e-01 | -4.590698e-01 | ... | -7.636587e-01 | -3.594213e-01 | -7.364778e-01 | -3.407777e-01 | -9.746319e-01 | -7.843190e-01 | -4.936135e-01 | -9.122278e-01 | -5.221280e-01 | -5.593176e-01 |
| 50% | 2.987991e-03 | 1.325260e-01 | -7.428113e-03 | 4.092062e-02 | 1.295254e-01 | -7.769156e-02 | -6.552493e-01 | -2.416550e-01 | -5.028017e-01 | -4.590698e-01 | ... | -6.238572e-01 | -3.594213e-01 | -3.533191e-02 | -3.407777e-01 | 4.828706e-01 | -1.938317e-01 | -4.936135e-01 | 3.275490e-01 | -5.221280e-01 | -5.593176e-01 |
| 75% | 8.753941e-01 | 9.392999e-01 | 1.027849e+00 | 6.113753e-01 | 1.295254e-01 | 8.818584e-01 | 1.031147e+00 | -2.416550e-01 | 1.848374e-01 | 3.599092e-02 | ... | 4.945548e-01 | -3.594213e-01 | 5.781708e-01 | 3.489160e-01 | 4.828706e-01 | 5.231886e-01 | 2.095146e-01 | 3.275490e-01 | -5.195420e-02 | 3.366657e-01 |
| max | 1.747800e+00 | 1.746074e+00 | 2.063126e+00 | 1.942436e+00 | 1.295254e-01 | 3.547275e+00 | 1.780656e+00 | 2.858916e+00 | 4.998311e+00 | 4.491538e+00 | ... | 3.290585e+00 | 4.275125e+00 | 1.951248e+00 | 1.759126e+01 | 3.397876e+00 | 2.463361e+00 | 4.428283e+00 | 4.046880e+00 | 2.298915e+00 | 3.920599e+00 |
8 rows × 22 columns
Una vez tenemos ya los datos recategorizados y estandarizados, podemos realizar el correlograma ya con todas las variables.
mcorr = xscal.corr()
sns.set(style="white")
plt.figure(figsize=(17, 17))
sns.heatmap(mcorr, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.subplots_adjust(bottom=0.2)
plt.title('Matriz de Correlaciones')
plt.show()
Para este ejercicio, se nos pide explorar distintos métodos para reducir dimensiones hasta explicar el 95% de la varianza. Para ello emplearemos el método del PCA, ya que intentando emplear también el método del Kernel PCA la RAM de mi ordenador colapsa.
pca = PCA()
xpca = pca.fit_transform(xscal)
acvar = pca.explained_variance_ratio_.cumsum()
acvar
array([0.13334801, 0.22519792, 0.30011693, 0.3722927 , 0.43419729,
0.48989342, 0.54398442, 0.58952102, 0.63451674, 0.67872925,
0.7178695 , 0.75620602, 0.7913338 , 0.82431083, 0.85597191,
0.88733438, 0.9160873 , 0.93857967, 0.95828155, 0.97495829,
0.99045464, 1. ])
He realizado un PCA con los datos recategorizados y estandarizados, y como se puede observar en el output superior, tenemos la suma acumulada de las varianzas a medida que vamos añadiendo dimensiones, en este caso vemos como en la dimension nº18 tenemos un 93,85% de la varianza acumulada que ya sería suficiente, pero como nos dicen que tenemos que llegar a un 95%, tendríamos que utilizar 19 dimensiones.
ndim = (acvar >= 0.95).argmax() + 1
plt.figure(figsize=(5, 5))
plt.plot(range(1, len(acvar) + 1), acvar, marker='o')
plt.axvline(ndim, color='red', linestyle='dashed', label=f'{ndim} Dimensiones')
plt.xlabel('Número de Dimensiones')
plt.ylabel('Varianza Acumulativa Explicada')
plt.legend()
plt.show()
En el gráfico superior, tenemos una representación visual de lo que he mencionado antes, en el que se demuestra que son 19 las dimensiones necesarias para llegar al 95% de la varianza explicada.
Para este ejercicio se nos pide utilizar el método de reducción de dimensiones seleccionado para identificar y representar outliers visualmente. Para llevar esto a cabo utilizaremos ahora sí tanto el PCA como el Kernel PCA aplicándoles 2 componentes y utilizando el EllipticEnvelope para la detección de los outliers.
pca2 = PCA(n_components=2)
respca = pca2.fit_transform(xscal)
cp = pd.DataFrame(data=respca, columns=['Componente_1', 'Componente_2'])
outlier_model = EllipticEnvelope(contamination=0.05)
outlier_model.fit(cp)
outliers = outlier_model.predict(cp) == -1
plt.figure(figsize=(7, 7))
sns.scatterplot(x='Componente_1', y='Componente_2', data=cp, hue=outliers, palette={0: 'blue', 1: 'red'}, legend='full')
plt.title('Visualización de Outliers en el Espacio PCA')
plt.show()
En este primer gráfico utilizando el PCA para la detección de outliers, hemos hecho que aquellos valores que sean outliers estén representados en rojo, se puede apreciar como en este caso la elliptic envelope corta casi por el componente 6, haciendo que todos los valores que estén fuera de la curva, sean outliers.
Podemos identificar dentro de todos los outliers que hay como dos grupos de ellos, por un lado, los cercanos al valor 6 del componente 1 y entre los valores -2 y 2 del componente 2. Por el otro lado, los cercanos al valor 6 del componente 2 y entre -2 y 0 del componente 1. Se puede observar como hay muchos más elementos en el primer grupo mencionado y también algún outlier suelto.
kpca = KernelPCA(n_components=2, kernel='rbf')
reskpca = kpca.fit_transform(xscal)
dfkpca = pd.DataFrame(data=reskpca, columns=['Componente_1', 'Componente_2'])
outlier_model = EllipticEnvelope(contamination=0.05)
outlier_model.fit(dfkpca)
outliers = outlier_model.predict(dfkpca) == -1
plt.figure(figsize=(7, 7))
sns.scatterplot(x='Componente_1', y='Componente_2', data=dfkpca, hue=outliers, palette={0: 'blue', 1: 'red'}, legend='full')
plt.title('Visualización de Outliers en el Espacio Kernel PCA')
plt.show()
Empleando el kernel pca, podemos ver como los valores de los ejes están mucho más concentrados alrededor del 0 y hay muchos menos outliers que en el caso anterior.
Se pueden apreciar 3 valores atípicos en la parte inferior del gráfico cercanos al valor -0.2 del componente 1 y con valores negativos rondando el -0.5 del componente 2. En la parte superior del gráfico es donde se encuentra el gran grueso de outliers, cortando la elliptic envelope en un valor cercano al valor 0.2 del componente 2 y entre los valores -0.4 y 0.1 del componente 1.
Para este ejercicio se nos pide aplicar el t-SNE o UMAP para una visualización en 2D y analizar las posibilidades de diferenciar tipos de accidentes, para aplicarlo he seleccionado aquellas variables que considero que pueden ser relevantes a la hora de elegir los tipos de accidente, que son: 'c_sev', 'c_vehs', 'c_conf', 'c_rcfg', 'c_wthr', 'c_rsur', 'c_raln', 'p_age', y 'p_isev'.
relcol = ['c_sev', 'c_vehs', 'c_conf', 'c_rcfg', 'c_wthr', 'c_rsur', 'c_raln', 'p_age', 'p_isev']
xrel = xscal[relcol]
tsne = TSNE(n_components=2, random_state=42)
reltsne = tsne.fit_transform(xrel)
xtsne = pd.DataFrame(data=reltsne, columns=['Dimensión_1', 'Dimensión_2'])
xtsne['Tipo_Accidente'] = xscal['c_conf']
plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='Tipo_Accidente', data=xtsne, palette='viridis', legend='full')
plt.title('Visualización t-SNE de Tipos de Accidentes en 2D')
plt.show()
Como podemos ver en el gráfico superior, a simple vista si se puede diferenciar algún grupo claro, fruto de la combinación de los diferentes tipos de puntos que vemos en la leyenda. Sin embargo, a continuación realizaremos lo mismo pero utilizando el UMAP a ver si hay algún cambio.
umap = UMAP(n_components=2, random_state=42)
relumap = umap.fit_transform(xrel)
xumap = pd.DataFrame(data=relumap, columns=['Dimensión_1', 'Dimensión_2'])
xumap['Tipo_Accidente'] = xscal['c_conf']
plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='Tipo_Accidente', data=xumap, palette='viridis', legend='full')
plt.title('Visualización UMAP de Tipos de Accidentes en 2D')
plt.show()
/usr/local/lib/python3.10/dist-packages/umap/umap_.py:1943: UserWarning: n_jobs value -1 overridden to 1 by setting random_state. Use no seed for parallelism.
warn(f"n_jobs value {self.n_jobs} overridden to 1 by setting random_state. Use no seed for parallelism.")
En este segundo gráfico utilizando el UMAP se puede ver que hay algún outlier que no pertenece a ningún grupo y están solos y luego por la zona central hay como un grupo de datos que no pertenece a ninguno de los grupos de alrededor y están como conexiones. Sin embargo, en el grupo solo podíamos diferenciar 4 o 5 grupos claros, pero mediante el uso del UMAP podemos distinguir más de 8 grupos grandes ya que no hay tantos puntos dispersos en la zona central del gráfico
Para este ejercicio se nos pide aplicar y analizar algoritmos de clustering como K-means, DBSCAN, o Agglomerative Clustering. Para este ejercicio utilizaré las variables seleccionadas en el ejercicio anterior que había considerado relevantes que son: 'c_sev', 'c_vehs', 'c_conf', 'c_rcfg', 'c_wthr', 'c_rsur', 'c_raln', 'p_age', y 'p_isev'.
kmeans = KMeans(n_clusters=3, random_state=42)
xumap['KMeans_Clusters'] = kmeans.fit_predict(xumap)
plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='KMeans_Clusters', data=xumap, palette='viridis', legend='full')
plt.title('Resultados de K-means Clustering')
plt.show()
/usr/local/lib/python3.10/dist-packages/sklearn/cluster/_kmeans.py:870: FutureWarning: The default value of `n_init` will change from 10 to 'auto' in 1.4. Set the value of `n_init` explicitly to suppress the warning warnings.warn(
En este primer caso, le hemos aplicado un k-means a los datos que habíamos utilizado previamente en el UMAP. En esta gráfica podemos observar 3 grandes clusters diferenciados claramente, cada uno en sus respectivas regiones de su color: azul, amarillo y morado.
dbscan = DBSCAN(eps=0.5, min_samples=3)
xumap['DBSCAN_Clusters'] = dbscan.fit_predict(xumap)
plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='DBSCAN_Clusters', data=xumap, palette='viridis')
plt.title('Resultados de DBSCAN Clustering')
plt.show()
En este segundo caso, le hemos aplicado un dbscan a los datos que habíamos utilizado previamente en el UMAP. En esta gráfica podemos observar que se han formado 6 grupos por las distintas densidades: 0, 20, 40, 60, 80 y 100. Pero no se pueden diferenciar de manera clara los grupos ya que hay muchos puntos que se sobreponen unos de otros. Lo que si que se aprecia es que hay muchos datos pertenecientes al cluster 0, al 20 y al 40. unos cuantos pertenecientes al 60 y muy pocos de los clusteres 80 y 100.
agg_clustering = AgglomerativeClustering(n_clusters=3)
xumap['Agg_Clusters'] = agg_clustering.fit_predict(xumap)
plt.figure(figsize=(12, 8))
sns.scatterplot(x='Dimensión_1', y='Dimensión_2', hue='Agg_Clusters', data=xumap, palette='viridis', legend='full')
plt.title('Resultados de Agglomerative Clustering')
plt.show()
En este tercer y último caso, le hemos aplicado un agglomerative clustering a los datos que habíamos utilizado previamente en el UMAP. En esta gráfica podemos observar que también hay 3 clusters como ocurría en el primer caso: azul, amarillo y morado. Sin embargo, a diferencia del clustering realizado por k-medias en el que los grupos se diferenciaban claramente, en este gráfico están los grupos mezclados, sobreponiéndose unos encima de otros y no presenta una estructura clara, por lo que considero que la mejor opción será el uso de un k-means.
Para este último ejercicio se nos pide dividir el conjunto de datos en entrenamiento y test, y usar el t-SNE o UMAP para visualizar y comparar las distribuciones.
xtrain, xtest = train_test_split(xrel, test_size=0.2, random_state=42)
tsne_train = TSNE(n_components=2, random_state=42)
xtsne_trainres = tsne_train.fit_transform(xtrain)
tsne_test = TSNE(n_components=2, random_state=42)
xtsne_testres = tsne_test.fit_transform(xtest)
plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
sns.scatterplot(x=xtsne_trainres[:, 0], y=xtsne_trainres[:, 1], palette='viridis', legend='full')
plt.title('t-SNE en Conjunto de Entrenamiento')
plt.subplot(1, 2, 2)
sns.scatterplot(x=xtsne_testres[:, 0], y=xtsne_testres[:, 1], palette='viridis', legend='full')
plt.title('t-SNE en Conjunto de Prueba')
plt.show()
<ipython-input-20-50cde3800ffc>:11: UserWarning: Ignoring `palette` because no `hue` variable has been assigned. sns.scatterplot(x=xtsne_trainres[:, 0], y=xtsne_trainres[:, 1], palette='viridis', legend='full') <ipython-input-20-50cde3800ffc>:15: UserWarning: Ignoring `palette` because no `hue` variable has been assigned. sns.scatterplot(x=xtsne_testres[:, 0], y=xtsne_testres[:, 1], palette='viridis', legend='full')
umap_train = UMAP(n_components=2, random_state=42)
xumap_trainres = umap_train.fit_transform(xtrain)
umap_test = UMAP(n_components=2, random_state=42)
xumap_testres = umap_test.fit_transform(xtest)
plt.figure(figsize=(16, 8))
plt.subplot(1, 2, 1)
sns.scatterplot(x=xumap_trainres[:, 0], y=xumap_trainres[:, 1], palette='viridis', legend='full')
plt.title('UMAP en Conjunto de Entrenamiento')
plt.subplot(1, 2, 2)
sns.scatterplot(x=xumap_testres[:, 0], y=xumap_testres[:, 1], palette='viridis', legend='full')
plt.title('UMAP en Conjunto de Prueba')
plt.show()
/usr/local/lib/python3.10/dist-packages/umap/umap_.py:1943: UserWarning: n_jobs value -1 overridden to 1 by setting random_state. Use no seed for parallelism.
warn(f"n_jobs value {self.n_jobs} overridden to 1 by setting random_state. Use no seed for parallelism.")
/usr/local/lib/python3.10/dist-packages/umap/umap_.py:1943: UserWarning: n_jobs value -1 overridden to 1 by setting random_state. Use no seed for parallelism.
warn(f"n_jobs value {self.n_jobs} overridden to 1 by setting random_state. Use no seed for parallelism.")
<ipython-input-21-30c966440ecf>:9: UserWarning: Ignoring `palette` because no `hue` variable has been assigned.
sns.scatterplot(x=xumap_trainres[:, 0], y=xumap_trainres[:, 1], palette='viridis', legend='full')
<ipython-input-21-30c966440ecf>:13: UserWarning: Ignoring `palette` because no `hue` variable has been assigned.
sns.scatterplot(x=xumap_testres[:, 0], y=xumap_testres[:, 1], palette='viridis', legend='full')
En ambos gráficos realizados tanto del tsne como del UMAP, en el conjunto de prueba (test) se pueden distinguir unos pocos grupos claramente, ya que a pesar de que se formen un menor número de grupos los datos están más agrupados.
Sin embargo, en el conjunto de datos de entrenamiento (train) los datos si es verdad que forman un mayor número de grupos pero hay muchos datos que se quedan dispersos por el medio o a medio camino de formar parte de alguno de los grupos, por lo que creo que debería entrenarse un poco más el modelo para que realmente se puedan llegar a formar grupos en condiciones que puedan ser analizados.